package com.glq1218.auth.service.impl;


import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.glq1218.auth.bo.AuthBO;
import com.glq1218.auth.bo.LoginBO;
import com.glq1218.auth.bo.RegisterBO;
import com.glq1218.auth.service.AuthenticationService;
import com.glq1218.domain.LoginUser;
import com.glq1218.permission.mapper.PermissionMapper;
import com.glq1218.user.entity.User;
import com.glq1218.user.mapper.UserMapper;
import com.glq1218.user.service.UserService;
import com.glq1218.util.JwtUtils;
import com.glq1218.util.RedisUtils;
import com.glq1218.util.SecurityUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.concurrent.TimeUnit;

/**
 * @author glq1218
 */
@Service("authenticationService")
@RequiredArgsConstructor
public class AuthenticationServiceImpl implements AuthenticationService {
    private final RedisUtils redisUtils;

    private final AuthenticationManager authenticationManager;

    private final PermissionMapper permissionMapper;

    private final PasswordEncoder passwordEncoder;

    private final UserService userService;

    private final UserMapper userMapper;

    // private final MenuService menuService;

    @Value("${login.only-one-login}")
    private boolean onlyOneLogin;

    @Override
    public AuthBO authenticate(LoginBO loginBO) {
        Authentication authentication = authenticationManager.authenticate(
                UsernamePasswordAuthenticationToken.unauthenticated(
                        loginBO.getUsername(),
                        loginBO.getPassword()
                )
        );
        // 把认证信息存入SecurityContextHolder
        SecurityUtils.getSecurityContext().setAuthentication(authentication);
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        String username = loginUser.getUsername();
        String jwt = JwtUtils.generateJwt(username);
        StringBuilder redisKey = new StringBuilder("loginUser:" + username);
        if (onlyOneLogin) {
            redisKey.append(jwt);
        }
        // 把用户信息存入redis
        redisUtils.put(
                redisKey.toString(),
                loginUser,
                12L,
                TimeUnit.HOURS);
        // 获取登录设备信息
        // HttpServletRequest request = ServletUtils.getRequest();
        // UserAgent userAgent = UserAgentUtil.parse(request.getHeader(Header.USER_AGENT.toString()));
        // String osName = userAgent.getOs().getName();
        // String osVersion = userAgent.getOsVersion();
        // String browser = userAgent.getBrowser().getName();
        // boolean mobile = userAgent.getBrowser().isMobile();
        // String ipAddress = ServletUtils.getIpAddress(request);
        // OnlineUser onlineUser = OnlineUser.builder()
        //         .username(authenticatedUser.getUsername())
        //         .osName(osName)
        //         .osVersion(osVersion)
        //         .browser(browser)
        //         .ipAddress(ipAddress)
        //         .mobile(mobile)
        //         .build();
        // todo 后续在考虑是否需要将登录的用户信息存入redis
        /*redisUtils.put("onlineUser:" + jwt, onlineUser);*/
        return AuthBO
                .builder()
                .token(jwt)
                .loginUser(loginUser)
                .build();
    }

    @Override
    public String register(RegisterBO registerBO) {

        String username = registerBO.getUsername();
        String password = registerBO.getPassword();
        String confirmPassword = registerBO.getRePassword();
        String phone = registerBO.getPhone();
        if (!StringUtils.hasText(username)) {
            throw new RuntimeException("用户名不能为空");
        }
        if (!StringUtils.hasText(password)) {
            throw new RuntimeException("密码不能为空");
        }
        if (!StringUtils.hasText(confirmPassword)) {
            throw new RuntimeException("确认密码不能为空");
        }
        if (!password.equals(confirmPassword)) {
            throw new RuntimeException("两次输入的密码不一致");
        }
        if (!StringUtils.hasText(phone)) {
            throw new RuntimeException("请输入手机号");
        }
        // 判断用户名是否已经存在
        if (userService.isExist(User::getUsername, username)) {
            throw new RuntimeException("用户名已存在");
        }
        if (userService.isExist(User::getPhone, phone)) {
            throw new RuntimeException("手机号已注册");
        }
        User user = User.builder()
                .username(username)
                .password(passwordEncoder.encode(password))
                .phone(phone)
                .build();
        if (!userService.save(user)) {
            throw new RuntimeException("注册失败，请联系管理员");
        }
        return "注册成功";
    }

    /**
     * 退出登录
     */
    @Override
    public void logout() {
        Object principal = SecurityUtils.getAuthentication().getPrincipal();
        SecurityContextHolder.clearContext();
        redisUtils.delete("");
    }

    /**
     * @param phone 手机号
     * @return 是否正确
     */
    @Override
    public String forget(String username, String phone, String password) {
        LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(User::getUsername, username);
        User user = userMapper.selectOne(lambdaUpdateWrapper);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }
        if (phone.equals(user.getPhone())) {
            user.setPassword(passwordEncoder.encode(password));
            int update = userMapper.updateById(user);
            if (update > 0) {
                return "密码修改成功";
            }
            return "密码修改失败";
        } else {
            throw new RuntimeException("手机号不正确");
        }
    }

    /**
     * @param oldPassword 旧密码
     * @param newPassword 新密码
     * @return 是否修改成功
     */
    @Override
    public String updatePassword(String oldPassword, String newPassword) {
        LoginUser user = SecurityUtils.getLoginUser();
        if (oldPassword.equals(user.getPassword())) {
            boolean is = userMapper.updatePassword(user.getId(), newPassword) > 0;
            return is ? "修改成功" : "修改失败";
        }
        throw new RuntimeException("旧密码错误");
    }
}
